home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 4: GNU Archives / Linux Cubed Series 4 - GNU Archives.iso / gnu / cvs-1.8 / cvs-1 / cvs-1.8.1 / src / import.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-06  |  30.6 KB  |  1,208 lines

  1. /*
  2.  * Copyright (c) 1992, Brian Berliner and Jeff Polk
  3.  * Copyright (c) 1989-1992, Brian Berliner
  4.  * 
  5.  * You may distribute under the terms of the GNU General Public License as
  6.  * specified in the README file that comes with the CVS 1.4 kit.
  7.  * 
  8.  * "import" checks in the vendor release located in the current directory into
  9.  * the CVS source repository.  The CVS vendor branch support is utilized.
  10.  * 
  11.  * At least three arguments are expected to follow the options:
  12.  *    repository    Where the source belongs relative to the CVSROOT
  13.  *    VendorTag    Vendor's major tag
  14.  *    VendorReleTag    Tag for this particular release
  15.  *
  16.  * Additional arguments specify more Vendor Release Tags.
  17.  */
  18.  
  19. #include "cvs.h"
  20. #include "savecwd.h"
  21.  
  22. #define    FILE_HOLDER    ".#cvsxxx"
  23.  
  24. static char *get_comment PROTO((char *user));
  25. static int add_rcs_file PROTO((char *message, char *rcs, char *user, char *vtag,
  26.                  int targc, char *targv[]));
  27. static int expand_at_signs PROTO((char *buf, off_t size, FILE *fp));
  28. static int add_rev PROTO((char *message, char *rcs, char *vfile, char *vers));
  29. static int add_tags PROTO((char *rcs, char *vfile, char *vtag, int targc,
  30.              char *targv[]));
  31. static int import_descend PROTO((char *message, char *vtag, int targc, char *targv[]));
  32. static int import_descend_dir PROTO((char *message, char *dir, char *vtag,
  33.                    int targc, char *targv[]));
  34. static int process_import_file PROTO((char *message, char *vfile, char *vtag,
  35.                 int targc, char *targv[]));
  36. static int update_rcs_file PROTO((char *message, char *vfile, char *vtag, int targc,
  37.                 char *targv[], int inattic));
  38. static void add_log PROTO((int ch, char *fname));
  39.  
  40. static int repos_len;
  41. static char vhead[50];
  42. static char vbranch[50];
  43. static FILE *logfp;
  44. static char repository[PATH_MAX];
  45. static int conflicts;
  46. static int use_file_modtime;
  47. static char *keyword_opt = NULL;
  48.  
  49. static const char *const import_usage[] =
  50. {
  51.     "Usage: %s %s [-d] [-k subst] [-I ign] [-m msg] [-b branch]\n",
  52.     "    [-W spec] repository vendor-tag release-tags...\n",
  53.     "\t-d\tUse the file's modification time as the time of import.\n",
  54.     "\t-k sub\tSet default RCS keyword substitution mode.\n",
  55.     "\t-I ign\tMore files to ignore (! to reset).\n",
  56.     "\t-b bra\tVendor branch id.\n",
  57.     "\t-m msg\tLog message.\n",
  58.     "\t-W spec\tWrappers specification line.\n",
  59.     NULL
  60. };
  61.  
  62. int
  63. import (argc, argv)
  64.     int argc;
  65.     char **argv;
  66. {
  67.     char *message = NULL;
  68.     char tmpfile[L_tmpnam+1];
  69.     char *cp;
  70.     int i, c, msglen, err;
  71.     List *ulist;
  72.     Node *p;
  73.  
  74.     if (argc == -1)
  75.     usage (import_usage);
  76.  
  77.     ign_setup ();
  78.     wrap_setup ();
  79.  
  80.     (void) strcpy (vbranch, CVSBRANCH);
  81.     optind = 1;
  82.     while ((c = getopt (argc, argv, "Qqdb:m:I:k:W:")) != -1)
  83.     {
  84.     switch (c)
  85.     {
  86.         case 'Q':
  87.         case 'q':
  88. #ifdef SERVER_SUPPORT
  89.         /* The CVS 1.5 client sends these options (in addition to
  90.            Global_option requests), so we must ignore them.  */
  91.         if (!server_active)
  92. #endif
  93.             error (1, 0,
  94.                "-q or -Q must be specified before \"%s\"",
  95.                command_name);
  96.         break;
  97.         case 'd':
  98.         use_file_modtime = 1;
  99.         break;
  100.         case 'b':
  101.         (void) strcpy (vbranch, optarg);
  102.         break;
  103.         case 'm':
  104. #ifdef FORCE_USE_EDITOR
  105.         use_editor = TRUE;
  106. #else
  107.         use_editor = FALSE;
  108. #endif
  109.         message = xstrdup(optarg);
  110.         break;
  111.         case 'I':
  112.         ign_add (optarg, 0);
  113.         break;
  114.             case 'k':
  115.         /* RCS_check_kflag returns strings of the form -kxx.  We
  116.            only use it for validation, so we can free the value
  117.            as soon as it is returned. */
  118.         free (RCS_check_kflag(optarg));    
  119.         keyword_opt = optarg;
  120.         break;
  121.         case 'W':
  122.         wrap_add (optarg, 0);
  123.         break;
  124.         case '?':
  125.         default:
  126.         usage (import_usage);
  127.         break;
  128.     }
  129.     }
  130.     argc -= optind;
  131.     argv += optind;
  132.     if (argc < 3)
  133.     usage (import_usage);
  134.  
  135.     for (i = 1; i < argc; i++)        /* check the tags for validity */
  136.     RCS_check_tag (argv[i]);
  137.  
  138.     /* XXX - this should be a module, not just a pathname */
  139.     if (! isabsolute (argv[0]))
  140.     {
  141.     if (CVSroot == NULL)
  142.     {
  143.         error (0, 0, "missing CVSROOT environment variable\n");
  144.         error (1, 0, "Set it or specify the '-d' option to %s.",
  145.            program_name);
  146.     }
  147.     (void) sprintf (repository, "%s/%s", CVSroot, argv[0]);
  148.     repos_len = strlen (CVSroot);
  149.     }
  150.     else
  151.     {
  152.     (void) strcpy (repository, argv[0]);
  153.     repos_len = 0;
  154.     }
  155.  
  156.     /*
  157.      * Consistency checks on the specified vendor branch.  It must be
  158.      * composed of only numbers and dots ('.').  Also, for now we only
  159.      * support branching to a single level, so the specified vendor branch
  160.      * must only have two dots in it (like "1.1.1").
  161.      */
  162.     for (cp = vbranch; *cp != '\0'; cp++)
  163.     if (!isdigit (*cp) && *cp != '.')
  164.         error (1, 0, "%s is not a numeric branch", vbranch);
  165.     if (numdots (vbranch) != 2)
  166.     error (1, 0, "Only branches with two dots are supported: %s", vbranch);
  167.     (void) strcpy (vhead, vbranch);
  168.     cp = strrchr (vhead, '.');
  169.     *cp = '\0';
  170.  
  171. #ifdef CLIENT_SUPPORT
  172.     if (client_active)
  173.     {
  174.     /* Do this now; don't ask for a log message if we can't talk to the
  175.        server.  But if there is a syntax error in the options, give
  176.        an error message without connecting.  */
  177.     start_server ();
  178.     }
  179. #endif
  180.  
  181.     if (use_editor)
  182.     {
  183.     do_editor ((char *) NULL, &message, repository,
  184.            (List *) NULL); 
  185.     }
  186.  
  187.     msglen = message == NULL ? 0 : strlen (message);
  188.     if (msglen == 0 || message[msglen - 1] != '\n')
  189.     {
  190.     char *nm = xmalloc (msglen + 2);
  191.     if (message != NULL)
  192.     {
  193.         (void) strcpy (nm, message);
  194.         free (message);
  195.     }
  196.     (void) strcat (nm + msglen, "\n");
  197.     message = nm;
  198.     }
  199.  
  200. #ifdef CLIENT_SUPPORT
  201.     if (client_active)
  202.     {
  203.     int err;
  204.  
  205.     if (use_file_modtime)
  206.         send_arg("-d");
  207.  
  208.     if (vbranch[0] != '\0')
  209.         option_with_arg ("-b", vbranch);
  210.     if (message)
  211.         option_with_arg ("-m", message);
  212.     if (keyword_opt != NULL)
  213.         option_with_arg ("-k", keyword_opt);
  214.     /* The only ignore processing which takes place on the server side
  215.        is the CVSROOT/cvsignore file.  But if the user specified -I !,
  216.        the documented behavior is to not process said file.  */
  217.     if (ign_inhibit_server)
  218.     {
  219.         send_arg ("-I");
  220.         send_arg ("!");
  221.     }
  222.  
  223.     {
  224.         int i;
  225.         for (i = 0; i < argc; ++i)
  226.         send_arg (argv[i]);
  227.     }
  228.  
  229.     logfp = stdin;
  230.     client_import_setup (repository);
  231.     err = import_descend (message, argv[1], argc - 2, argv + 2);
  232.     client_import_done ();
  233.     send_to_server ("import\012", 0);
  234.     err += get_responses_and_close ();
  235.     return err;
  236.     }
  237. #endif
  238.  
  239.     /*
  240.      * Make all newly created directories writable.  Should really use a more
  241.      * sophisticated security mechanism here.
  242.      */
  243.     (void) umask (cvsumask);
  244.     make_directories (repository);
  245.  
  246.     /* Create the logfile that will be logged upon completion */
  247.     if ((logfp = fopen (tmpnam (tmpfile), "w+")) == NULL)
  248.     error (1, errno, "cannot create temporary file `%s'", tmpfile);
  249.     (void) unlink (tmpfile);        /* to be sure it goes away */
  250.     (void) fprintf (logfp, "\nVendor Tag:\t%s\n", argv[1]);
  251.     (void) fprintf (logfp, "Release Tags:\t");
  252.     for (i = 2; i < argc; i++)
  253.     (void) fprintf (logfp, "%s\n\t\t", argv[i]);
  254.     (void) fprintf (logfp, "\n");
  255.  
  256.     /* Just Do It.  */
  257.     err = import_descend (message, argv[1], argc - 2, argv + 2);
  258.     if (conflicts)
  259.     {
  260.     if (!really_quiet)
  261.     {
  262.         char buf[80];
  263.         sprintf (buf, "\n%d conflicts created by this import.\n",
  264.              conflicts);
  265.         cvs_output (buf, 0);
  266.         cvs_output ("Use the following command to help the merge:\n\n",
  267.             0);
  268.         cvs_output ("\t", 1);
  269.         cvs_output (program_name, 0);
  270.         cvs_output (" checkout -j", 0);
  271.         cvs_output (argv[1], 0);
  272.         cvs_output (":yesterday -j", 0);
  273.         cvs_output (argv[1], 0);
  274.         cvs_output (" ", 1);
  275.         cvs_output (argv[0], 0);
  276.         cvs_output ("\n\n", 0);
  277.     }
  278.  
  279.     (void) fprintf (logfp, "\n%d conflicts created by this import.\n",
  280.             conflicts);
  281.     (void) fprintf (logfp,
  282.             "Use the following command to help the merge:\n\n");
  283.     (void) fprintf (logfp, "\t%s checkout -j%s:yesterday -j%s %s\n\n",
  284.             program_name, argv[1], argv[1], argv[0]);
  285.     }
  286.     else
  287.     {
  288.     if (!really_quiet)
  289.         cvs_output ("\nNo conflicts created by this import\n\n", 0);
  290.     (void) fprintf (logfp, "\nNo conflicts created by this import\n\n");
  291.     }
  292.  
  293.     /*
  294.      * Write out the logfile and clean up.
  295.      */
  296.     ulist = getlist ();
  297.     p = getnode ();
  298.     p->type = UPDATE;
  299.     p->delproc = update_delproc;
  300.     p->key = xstrdup ("- Imported sources");
  301.     p->data = (char *) T_TITLE;
  302.     (void) addnode (ulist, p);
  303.     Update_Logfile (repository, message, vbranch, logfp, ulist);
  304.     dellist (&ulist);
  305.     (void) fclose (logfp);
  306.  
  307.     /* Make sure the temporary file goes away, even on systems that don't let
  308.        you delete a file that's in use.  */
  309.     unlink (tmpfile);
  310.  
  311.     if (message)
  312.     free (message);
  313.  
  314.     return (err);
  315. }
  316.  
  317. /*
  318.  * process all the files in ".", then descend into other directories.
  319.  */
  320. static int
  321. import_descend (message, vtag, targc, targv)
  322.     char *message;
  323.     char *vtag;
  324.     int targc;
  325.     char *targv[];
  326. {
  327.     DIR *dirp;
  328.     struct dirent *dp;
  329.     int err = 0;
  330.     List *dirlist = NULL;
  331.  
  332.     /* first, load up any per-directory ignore lists */
  333.     ign_add_file (CVSDOTIGNORE, 1);
  334.     wrap_add_file (CVSDOTWRAPPER, 1);
  335.  
  336.     if ((dirp = opendir (".")) == NULL)
  337.     {
  338.     err++;
  339.     }
  340.     else
  341.     {
  342.     while ((dp = readdir (dirp)) != NULL)
  343.     {
  344.         if (strcmp (dp->d_name, ".") == 0 || strcmp (dp->d_name, "..") == 0)
  345.         continue;
  346. #ifdef SERVER_SUPPORT
  347.         /* CVS directories are created in the temp directory by
  348.            server.c because it doesn't special-case import.  So
  349.            don't print a message about them, regardless of -I!.  */
  350.         if (server_active && strcmp (dp->d_name, CVSADM) == 0)
  351.         continue;
  352. #endif
  353.         if (ign_name (dp->d_name))
  354.         {
  355.         add_log ('I', dp->d_name);
  356.         continue;
  357.         }
  358.  
  359.         if (
  360. #ifdef DT_DIR
  361.         (dp->d_type == DT_DIR
  362.          || (dp->d_type == DT_UNKNOWN && isdir (dp->d_name)))
  363. #else
  364.         isdir (dp->d_name)
  365. #endif
  366.         && !wrap_name_has (dp->d_name, WRAP_TOCVS)
  367.         )
  368.             {    
  369.         Node *n;
  370.  
  371.         if (dirlist == NULL)
  372.             dirlist = getlist();
  373.  
  374.         n = getnode();
  375.         n->key = xstrdup (dp->d_name);
  376.         addnode(dirlist, n);
  377.         }
  378.         else if (
  379. #ifdef DT_DIR
  380.         dp->d_type == DT_LNK || dp->d_type == DT_UNKNOWN && 
  381. #endif
  382.         islink (dp->d_name))
  383.         {
  384.         add_log ('L', dp->d_name);
  385.         err++;
  386.         }
  387.         else
  388.         {
  389. #ifdef CLIENT_SUPPORT
  390.         if (client_active)
  391.             err += client_process_import_file (message, dp->d_name,
  392.                                vtag, targc, targv,
  393.                                repository);
  394.         else
  395. #endif
  396.             err += process_import_file (message, dp->d_name,
  397.                         vtag, targc, targv);
  398.         }
  399.     }
  400.     (void) closedir (dirp);
  401.     }
  402.  
  403.     if (dirlist != NULL) 
  404.     {
  405.     Node *head, *p;
  406.  
  407.     head = dirlist->list;
  408.     for (p = head->next; p != head; p = p->next)
  409.     {
  410.         err += import_descend_dir (message, p->key, vtag, targc, targv);
  411.     }
  412.  
  413.     dellist(&dirlist);
  414.     }
  415.  
  416.     return (err);
  417. }
  418.  
  419. /*
  420.  * Process the argument import file.
  421.  */
  422. static int
  423. process_import_file (message, vfile, vtag, targc, targv)
  424.     char *message;
  425.     char *vfile;
  426.     char *vtag;
  427.     int targc;
  428.     char *targv[];
  429. {
  430.     char attic_name[PATH_MAX];
  431.     char rcs[PATH_MAX];
  432.     int inattic = 0;
  433.  
  434.     (void) sprintf (rcs, "%s/%s%s", repository, vfile, RCSEXT);
  435.     if (!isfile (rcs))
  436.     {
  437.     (void) sprintf (attic_name, "%s/%s/%s%s", repository, CVSATTIC,
  438.             vfile, RCSEXT);
  439.     if (!isfile (attic_name))
  440.     {
  441.  
  442.         /*
  443.          * A new import source file; it doesn't exist as a ,v within the
  444.          * repository nor in the Attic -- create it anew.
  445.          */
  446.         add_log ('N', vfile);
  447.         return (add_rcs_file (message, rcs, vfile, vtag, targc, targv));
  448.     }
  449.     inattic = 1;
  450.     }
  451.  
  452.     /*
  453.      * an rcs file exists. have to do things the official, slow, way.
  454.      */
  455.     return (update_rcs_file (message, vfile, vtag, targc, targv, inattic));
  456. }
  457.  
  458. /*
  459.  * The RCS file exists; update it by adding the new import file to the
  460.  * (possibly already existing) vendor branch.
  461.  */
  462. static int
  463. update_rcs_file (message, vfile, vtag, targc, targv, inattic)
  464.     char *message;
  465.     char *vfile;
  466.     char *vtag;
  467.     int targc;
  468.     char *targv[];
  469.     int inattic;
  470. {
  471.     Vers_TS *vers;
  472.     int letter;
  473.     int ierrno;
  474.     char *tmpdir;
  475.     char *tocvsPath;
  476.  
  477.     vers = Version_TS (repository, (char *) NULL, vbranch, (char *) NULL, vfile,
  478.                1, 0, (List *) NULL, (RCSNode *) NULL);
  479.     if (vers->vn_rcs != NULL
  480.     && !RCS_isdead(vers->srcfile, vers->vn_rcs))
  481.     {
  482.     char xtmpfile[PATH_MAX];
  483.     int different;
  484.     int retcode = 0;
  485.  
  486.     tmpdir = getenv ("TMPDIR");
  487.     if (tmpdir == NULL || tmpdir[0] == '\0') 
  488.       tmpdir = "/tmp";
  489.  
  490.     (void) sprintf (xtmpfile, "%s/cvs-imp%ld", tmpdir, (long) getpid());
  491.  
  492.     /*
  493.      * The rcs file does have a revision on the vendor branch. Compare
  494.      * this revision with the import file; if they match exactly, there
  495.      * is no need to install the new import file as a new revision to the
  496.      * branch.  Just tag the revision with the new import tags.
  497.      * 
  498.      * This is to try to cut down the number of "C" conflict messages for
  499.      * locally modified import source files.
  500.      */
  501.     /* Why is RCS_FLAGS_FORCE here?  I wouldn't think that it would have any
  502.        effect in conjunction with passing NULL for workfile (i.e. to stdout).  */
  503.     retcode = RCS_checkout (vers->srcfile->path, NULL, vers->vn_rcs,
  504. #ifdef HAVE_RCS5
  505.                             "-ko",
  506. #else
  507.                             NULL,
  508. #endif
  509.                             xtmpfile, RCS_FLAGS_FORCE, 0);
  510.     if (retcode != 0)
  511.     {
  512.         ierrno = errno;
  513.         fperror (logfp, 0, retcode == -1 ? ierrno : 0,
  514.              "ERROR: cannot co revision %s of file %s", vers->vn_rcs,
  515.              vers->srcfile->path);
  516.         error (0, retcode == -1 ? ierrno : 0,
  517.            "ERROR: cannot co revision %s of file %s", vers->vn_rcs,
  518.            vers->srcfile->path);
  519.         (void) unlink_file (xtmpfile);
  520.         return (1);
  521.     }
  522.  
  523.     tocvsPath = wrap_tocvs_process_file (vfile);
  524.     different = xcmp (xtmpfile, vfile);
  525.     if (tocvsPath)
  526.         if (unlink_file_dir (tocvsPath) < 0)
  527.         error (0, errno, "cannot remove %s", tocvsPath);
  528.  
  529.     (void) unlink_file (xtmpfile);
  530.     if (!different)
  531.     {
  532.         int retval = 0;
  533.  
  534.         /*
  535.          * The two files are identical.  Just update the tags, print the
  536.          * "U", signifying that the file has changed, but needs no
  537.          * attention, and we're done.
  538.          */
  539.         if (add_tags (vers->srcfile->path, vfile, vtag, targc, targv))
  540.         retval = 1;
  541.         add_log ('U', vfile);
  542.         freevers_ts (&vers);
  543.         return (retval);
  544.     }
  545.     }
  546.  
  547.     /* We may have failed to parse the RCS file; check just in case */
  548.     if (vers->srcfile == NULL ||
  549.     add_rev (message, vers->srcfile->path, vfile, vers->vn_rcs) ||
  550.     add_tags (vers->srcfile->path, vfile, vtag, targc, targv))
  551.     {
  552.     freevers_ts (&vers);
  553.     return (1);
  554.     }
  555.  
  556.     if (vers->srcfile->branch == NULL || inattic ||
  557.     strcmp (vers->srcfile->branch, vbranch) != 0)
  558.     {
  559.     conflicts++;
  560.     letter = 'C';
  561.     }
  562.     else
  563.     letter = 'U';
  564.     add_log (letter, vfile);
  565.  
  566.     freevers_ts (&vers);
  567.     return (0);
  568. }
  569.  
  570. /*
  571.  * Add the revision to the vendor branch
  572.  */
  573. static int
  574. add_rev (message, rcs, vfile, vers)
  575.     char *message;
  576.     char *rcs;
  577.     char *vfile;
  578.     char *vers;
  579. {
  580.     int locked, status, ierrno;
  581.     char *tocvsPath;
  582.  
  583.     if (noexec)
  584.     return (0);
  585.  
  586.     locked = 0;
  587.     if (vers != NULL)
  588.     {
  589.     /* Before RCS_lock existed, we were directing stdout, as well as
  590.        stderr, from the RCS command, to DEVNULL.  I wouldn't guess that
  591.        was necessary, but I don't know for sure.  */
  592.         if (RCS_lock (rcs, vbranch, 1) != 0)
  593.     {
  594.         error (0, errno, "fork failed");
  595.         return (1);
  596.     }
  597.     locked = 1;
  598.     }
  599.     tocvsPath = wrap_tocvs_process_file (vfile);
  600.     if (tocvsPath == NULL)
  601.     {
  602.     /* We play with hard links rather than passing -u to ci to avoid
  603.        expanding RCS keywords (see test 106.5 in sanity.sh).  */
  604.     if (link_file (vfile, FILE_HOLDER) < 0)
  605.     {
  606.         if (errno == EEXIST)
  607.         {
  608.         (void) unlink_file (FILE_HOLDER);
  609.         (void) link_file (vfile, FILE_HOLDER);
  610.         }
  611.         else
  612.         {
  613.         ierrno = errno;
  614.         fperror (logfp, 0, ierrno,
  615.              "ERROR: cannot create link to %s", vfile);
  616.         error (0, ierrno, "ERROR: cannot create link to %s", vfile);
  617.         return (1);
  618.         }
  619.     }
  620.     }
  621.  
  622.     status = RCS_checkin (rcs, tocvsPath == NULL ? vfile : tocvsPath,
  623.               message, vbranch,
  624.               (RCS_FLAGS_QUIET
  625.                | (use_file_modtime ? RCS_FLAGS_MODTIME : 0)),
  626.               0);
  627.     ierrno = errno;
  628.  
  629.     if (tocvsPath == NULL)
  630.     rename_file (FILE_HOLDER, vfile);
  631.     else
  632.     if (unlink_file_dir (tocvsPath) < 0)
  633.         error (0, errno, "cannot remove %s", tocvsPath);
  634.  
  635.     if (status)
  636.     {
  637.     if (!noexec)
  638.     {
  639.         fperror (logfp, 0, status == -1 ? ierrno : 0, "ERROR: Check-in of %s failed", rcs);
  640.         error (0, status == -1 ? ierrno : 0, "ERROR: Check-in of %s failed", rcs);
  641.     }
  642.     if (locked)
  643.     {
  644.         (void) RCS_unlock(rcs, vbranch, 0);
  645.     }
  646.     return (1);
  647.     }
  648.     return (0);
  649. }
  650.  
  651. /*
  652.  * Add the vendor branch tag and all the specified import release tags to the
  653.  * RCS file.  The vendor branch tag goes on the branch root (1.1.1) while the
  654.  * vendor release tags go on the newly added leaf of the branch (1.1.1.1,
  655.  * 1.1.1.2, ...).
  656.  */
  657. static int
  658. add_tags (rcs, vfile, vtag, targc, targv)
  659.     char *rcs;
  660.     char *vfile;
  661.     char *vtag;
  662.     int targc;
  663.     char *targv[];
  664. {
  665.     int i, ierrno;
  666.     Vers_TS *vers;
  667.     int retcode = 0;
  668.  
  669.     if (noexec)
  670.     return (0);
  671.  
  672.     if ((retcode = RCS_settag(rcs, vtag, vbranch)) != 0)
  673.     {
  674.     ierrno = errno;
  675.     fperror (logfp, 0, retcode == -1 ? ierrno : 0, 
  676.          "ERROR: Failed to set tag %s in %s", vtag, rcs);
  677.     error (0, retcode == -1 ? ierrno : 0,
  678.            "ERROR: Failed to set tag %s in %s", vtag, rcs);
  679.     return (1);
  680.     }
  681.     vers = Version_TS (repository, (char *) NULL, vtag, (char *) NULL, vfile,
  682.                1, 0, (List *) NULL, (RCSNode *) NULL);
  683.     for (i = 0; i < targc; i++)
  684.     {
  685.     if ((retcode = RCS_settag (rcs, targv[i], vers->vn_rcs)) != 0)
  686.     {
  687.         ierrno = errno;
  688.         fperror (logfp, 0, retcode == -1 ? ierrno : 0, 
  689.              "WARNING: Couldn't add tag %s to %s", targv[i], rcs);
  690.         error (0, retcode == -1 ? ierrno : 0,
  691.            "WARNING: Couldn't add tag %s to %s", targv[i], rcs);
  692.     }
  693.     }
  694.     freevers_ts (&vers);
  695.     return (0);
  696. }
  697.  
  698. /*
  699.  * Stolen from rcs/src/rcsfnms.c, and adapted/extended.
  700.  */
  701. struct compair
  702. {
  703.     char *suffix, *comlead;
  704. };
  705.  
  706. static const struct compair comtable[] =
  707. {
  708.  
  709. /*
  710.  * comtable pairs each filename suffix with a comment leader. The comment
  711.  * leader is placed before each line generated by the $Log keyword. This
  712.  * table is used to guess the proper comment leader from the working file's
  713.  * suffix during initial ci (see InitAdmin()). Comment leaders are needed for
  714.  * languages without multiline comments; for others they are optional.
  715.  */
  716.     {"a", "-- "},            /* Ada         */
  717.     {"ada", "-- "},
  718.     {"adb", "-- "},
  719.     {"asm", ";; "},            /* assembler (MS-DOS) */
  720.     {"ads", "-- "},            /* Ada         */
  721.     {"bas", "' "},                /* Visual Basic code */
  722.     {"bat", ":: "},            /* batch (MS-DOS) */
  723.     {"body", "-- "},            /* Ada         */
  724.     {"c", " * "},            /* C         */
  725.     {"c++", "// "},            /* C++ in all its infinite guises */
  726.     {"cc", "// "},
  727.     {"cpp", "// "},
  728.     {"cxx", "// "},
  729.     {"m", "// "},            /* Objective-C */
  730.     {"cl", ";;; "},            /* Common Lisp     */
  731.     {"cmd", ":: "},            /* command (OS/2) */
  732.     {"cmf", "c "},            /* CM Fortran     */
  733.     {"cs", " * "},            /* C*         */
  734.     {"csh", "# "},            /* shell     */
  735.     {"dlg", " * "},               /* MS Windows dialog file */
  736.     {"e", "# "},            /* efl         */
  737.     {"epsf", "% "},            /* encapsulated postscript */
  738.     {"epsi", "% "},            /* encapsulated postscript */
  739.     {"el", "; "},            /* Emacs Lisp     */
  740.     {"f", "c "},            /* Fortran     */
  741.     {"for", "c "},
  742.     {"frm", "' "},                /* Visual Basic form */
  743.     {"h", " * "},            /* C-header     */
  744.     {"hh", "// "},            /* C++ header     */
  745.     {"hpp", "// "},
  746.     {"hxx", "// "},
  747.     {"in", "# "},            /* for Makefile.in */
  748.     {"l", " * "},            /* lex (conflict between lex and
  749.                      * franzlisp) */
  750.     {"mac", ";; "},            /* macro (DEC-10, MS-DOS, PDP-11,
  751.                      * VMS, etc) */
  752.     {"mak", "# "},                /* makefile, e.g. Visual C++ */
  753.     {"me", ".\\\" "},            /* me-macros    t/nroff     */
  754.     {"ml", "; "},            /* mocklisp     */
  755.     {"mm", ".\\\" "},            /* mm-macros    t/nroff     */
  756.     {"ms", ".\\\" "},            /* ms-macros    t/nroff     */
  757.     {"man", ".\\\" "},            /* man-macros    t/nroff     */
  758.     {"1", ".\\\" "},            /* feeble attempt at man pages... */
  759.     {"2", ".\\\" "},
  760.     {"3", ".\\\" "},
  761.     {"4", ".\\\" "},
  762.     {"5", ".\\\" "},
  763.     {"6", ".\\\" "},
  764.     {"7", ".\\\" "},
  765.     {"8", ".\\\" "},
  766.     {"9", ".\\\" "},
  767.     {"p", " * "},            /* pascal     */
  768.     {"pas", " * "},
  769.     {"pl", "# "},            /* perl    (conflict with Prolog) */
  770.     {"ps", "% "},            /* postscript     */
  771.     {"psw", "% "},            /* postscript wrap */
  772.     {"pswm", "% "},            /* postscript wrap */
  773.     {"r", "# "},            /* ratfor     */
  774.     {"rc", " * "},            /* Microsoft Windows resource file */
  775.     {"red", "% "},            /* psl/rlisp     */
  776. #ifdef sparc
  777.     {"s", "! "},            /* assembler     */
  778. #endif
  779. #ifdef mc68000
  780.     {"s", "| "},            /* assembler     */
  781. #endif
  782. #ifdef pdp11
  783.     {"s", "/ "},            /* assembler     */
  784. #endif
  785. #ifdef vax
  786.     {"s", "# "},            /* assembler     */
  787. #endif
  788. #ifdef __ksr__
  789.     {"s", "# "},            /* assembler     */
  790.     {"S", "# "},            /* Macro assembler */
  791. #endif
  792.     {"sh", "# "},            /* shell     */
  793.     {"sl", "% "},            /* psl         */
  794.     {"spec", "-- "},            /* Ada         */
  795.     {"tex", "% "},            /* tex         */
  796.     {"y", " * "},            /* yacc         */
  797.     {"ye", " * "},            /* yacc-efl     */
  798.     {"yr", " * "},            /* yacc-ratfor     */
  799.     {"", "# "},                /* default for empty suffix     */
  800.     {NULL, "# "}            /* default for unknown suffix;     */
  801. /* must always be last         */
  802. };
  803.  
  804. static char *
  805. get_comment (user)
  806.     char *user;
  807. {
  808.     char *cp, *suffix;
  809.     char suffix_path[PATH_MAX];
  810.     int i;
  811.  
  812.     cp = strrchr (user, '.');
  813.     if (cp != NULL)
  814.     {
  815.     cp++;
  816.  
  817.     /*
  818.      * Convert to lower-case, since we are not concerned about the
  819.      * case-ness of the suffix.
  820.      */
  821.     (void) strcpy (suffix_path, cp);
  822.     for (cp = suffix_path; *cp; cp++)
  823.         if (isupper (*cp))
  824.         *cp = tolower (*cp);
  825.     suffix = suffix_path;
  826.     }
  827.     else
  828.     suffix = "";            /* will use the default */
  829.     for (i = 0;; i++)
  830.     {
  831.     if (comtable[i].suffix == NULL)    /* default */
  832.         return (comtable[i].comlead);
  833.     if (strcmp (suffix, comtable[i].suffix) == 0)
  834.         return (comtable[i].comlead);
  835.     }
  836. }
  837.  
  838. static int
  839. add_rcs_file (message, rcs, user, vtag, targc, targv)
  840.     char *message;
  841.     char *rcs;
  842.     char *user;
  843.     char *vtag;
  844.     int targc;
  845.     char *targv[];
  846. {
  847.     FILE *fprcs, *fpuser;
  848.     struct stat sb;
  849.     struct tm *ftm;
  850.     time_t now;
  851.     char altdate1[50];
  852. #ifndef HAVE_RCS5
  853.     char altdate2[50];
  854. #endif
  855.     char *author;
  856.     int i, ierrno, err = 0;
  857.     mode_t mode;
  858.     char *tocvsPath;
  859.     char *userfile;
  860.  
  861.     if (noexec)
  862.     return (0);
  863.  
  864.     /* FIXME?  We always import files as text files (note that means
  865.        that files get stored with straight linefeeds).  There isn't an
  866.        obvious, clean, way to let people specify which files are binary.
  867.        Maybe based on the file name....  */
  868.     tocvsPath = wrap_tocvs_process_file (user);
  869.     userfile = (tocvsPath == NULL ? user : tocvsPath);
  870.     fpuser = fopen (userfile, "r");
  871.     if (fpuser == NULL)
  872.     {
  873.     /* not fatal, continue import */
  874.     fperror (logfp, 0, errno, "ERROR: cannot read file %s", userfile);
  875.     error (0, errno, "ERROR: cannot read file %s", userfile);
  876.     goto read_error;
  877.     }
  878.     fprcs = fopen (rcs, "w+b");
  879.     if (fprcs == NULL)
  880.     {
  881.     ierrno = errno;
  882.     goto write_error_noclose;
  883.     }
  884.  
  885.     /*
  886.      * putadmin()
  887.      */
  888.     if (fprintf (fprcs, "head     %s;\012", vhead) < 0 ||
  889.     fprintf (fprcs, "branch   %s;\012", vbranch) < 0 ||
  890.     fprintf (fprcs, "access   ;\012") < 0 ||
  891.     fprintf (fprcs, "symbols  ") < 0)
  892.     {
  893.     goto write_error;
  894.     }
  895.  
  896.     for (i = targc - 1; i >= 0; i--)    /* RCS writes the symbols backwards */
  897.     if (fprintf (fprcs, "%s:%s.1 ", targv[i], vbranch) < 0)
  898.         goto write_error;
  899.  
  900.     if (fprintf (fprcs, "%s:%s;\012", vtag, vbranch) < 0 ||
  901.     fprintf (fprcs, "locks    ; strict;\012") < 0 ||
  902.     /* XXX - make sure @@ processing works in the RCS file */
  903.     fprintf (fprcs, "comment  @%s@;\012", get_comment (user)) < 0)
  904.     {
  905.     goto write_error;
  906.     }
  907.  
  908.     if (keyword_opt != NULL)
  909.       if (fprintf (fprcs, "expand   @%s@;\012", keyword_opt) < 0)
  910.     {
  911.       goto write_error;
  912.     }
  913.  
  914.     if (fprintf (fprcs, "\012") < 0)
  915.       goto write_error;
  916.  
  917.     /*
  918.      * puttree()
  919.      */
  920.     if (fstat (fileno (fpuser), &sb) < 0)
  921.     error (1, errno, "cannot fstat %s", user);
  922.     if (use_file_modtime)
  923.     now = sb.st_mtime;
  924.     else
  925.     (void) time (&now);
  926. #ifdef HAVE_RCS5
  927.     ftm = gmtime (&now);
  928. #else
  929.     ftm = localtime (&now);
  930. #endif
  931.     (void) sprintf (altdate1, DATEFORM,
  932.             ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
  933.             ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
  934.             ftm->tm_min, ftm->tm_sec);
  935. #ifdef HAVE_RCS5
  936. #define    altdate2 altdate1
  937. #else
  938.     /*
  939.      * If you don't have RCS V5 or later, you need to lie about the ci
  940.      * time, since RCS V4 and earlier insist that the times differ.
  941.      */
  942.     now++;
  943.     ftm = localtime (&now);
  944.     (void) sprintf (altdate2, DATEFORM,
  945.             ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
  946.             ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
  947.             ftm->tm_min, ftm->tm_sec);
  948. #endif
  949.     author = getcaller ();
  950.  
  951.     if (fprintf (fprcs, "\012%s\012", vhead) < 0 ||
  952.     fprintf (fprcs, "date     %s;  author %s;  state Exp;\012",
  953.          altdate1, author) < 0 ||
  954.     fprintf (fprcs, "branches %s.1;\012", vbranch) < 0 ||
  955.     fprintf (fprcs, "next     ;\012") < 0 ||
  956.     fprintf (fprcs, "\012%s.1\012", vbranch) < 0 ||
  957.     fprintf (fprcs, "date     %s;  author %s;  state Exp;\012",
  958.          altdate2, author) < 0 ||
  959.     fprintf (fprcs, "branches ;\012") < 0 ||
  960.     fprintf (fprcs, "next     ;\012\012") < 0 ||
  961.     /*
  962.      * putdesc()
  963.      */
  964.     fprintf (fprcs, "\012desc\012") < 0 ||
  965.     fprintf (fprcs, "@@\012\012\012") < 0 ||
  966.     /*
  967.      * putdelta()
  968.      */
  969.     fprintf (fprcs, "\012%s\012", vhead) < 0 ||
  970.     fprintf (fprcs, "log\012") < 0 ||
  971.     fprintf (fprcs, "@Initial revision\012@\012") < 0 ||
  972.     fprintf (fprcs, "text\012@") < 0)
  973.     {
  974.     goto write_error;
  975.     }
  976.  
  977.     /* Now copy over the contents of the file, expanding at signs.  */
  978.     {
  979.     unsigned char buf[8192];
  980.     unsigned int len;
  981.  
  982.     while (1)
  983.     {
  984.         len = fread (buf, 1, sizeof buf, fpuser);
  985.         if (len == 0)
  986.         {
  987.         if (ferror (fpuser))
  988.             error (1, errno, "cannot read file %s for copying", user);
  989.         break;
  990.         }
  991.         if (expand_at_signs (buf, len, fprcs) < 0)
  992.         goto write_error;
  993.     }
  994.     }
  995.     if (fprintf (fprcs, "@\012\012") < 0 ||
  996.     fprintf (fprcs, "\012%s.1\012", vbranch) < 0 ||
  997.     fprintf (fprcs, "log\012@") < 0 ||
  998.     expand_at_signs (message, (off_t) strlen (message), fprcs) < 0 ||
  999.     fprintf (fprcs, "@\012text\012") < 0 ||
  1000.     fprintf (fprcs, "@@\012") < 0)
  1001.     {
  1002.     goto write_error;
  1003.     }
  1004.     if (fclose (fprcs) == EOF)
  1005.     {
  1006.     ierrno = errno;
  1007.     goto write_error_noclose;
  1008.     }
  1009.     (void) fclose (fpuser);
  1010.  
  1011.     /*
  1012.      * Fix the modes on the RCS files.  The user modes of the original
  1013.      * user file are propagated to the group and other modes as allowed
  1014.      * by the repository umask, except that all write permissions are
  1015.      * turned off.
  1016.      */
  1017.     mode = (sb.st_mode |
  1018.         (sb.st_mode & S_IRWXU) >> 3 |
  1019.         (sb.st_mode & S_IRWXU) >> 6) &
  1020.        ~cvsumask &
  1021.        ~(S_IWRITE | S_IWGRP | S_IWOTH);
  1022.     if (chmod (rcs, mode) < 0)
  1023.     {
  1024.     ierrno = errno;
  1025.     fperror (logfp, 0, ierrno,
  1026.          "WARNING: cannot change mode of file %s", rcs);
  1027.     error (0, ierrno, "WARNING: cannot change mode of file %s", rcs);
  1028.     err++;
  1029.     }
  1030.     if (tocvsPath)
  1031.     if (unlink_file_dir (tocvsPath) < 0)
  1032.         error (0, errno, "cannot remove %s", tocvsPath);
  1033.     return (err);
  1034.  
  1035. write_error:
  1036.     ierrno = errno;
  1037.     (void) fclose (fprcs);
  1038. write_error_noclose:
  1039.     (void) fclose (fpuser);
  1040.     fperror (logfp, 0, ierrno, "ERROR: cannot write file %s", rcs);
  1041.     error (0, ierrno, "ERROR: cannot write file %s", rcs);
  1042.     if (ierrno == ENOSPC)
  1043.     {
  1044.     (void) unlink (rcs);
  1045.     fperror (logfp, 0, 0, "ERROR: out of space - aborting");
  1046.     error (1, 0, "ERROR: out of space - aborting");
  1047.     }
  1048. read_error:
  1049.     if (tocvsPath)
  1050.     if (unlink_file_dir (tocvsPath) < 0)
  1051.         error (0, errno, "cannot remove %s", tocvsPath);
  1052.  
  1053.     return (err + 1);
  1054. }
  1055.  
  1056. /*
  1057.  * Write SIZE bytes at BUF to FP, expanding @ signs into double @
  1058.  * signs.  If an error occurs, return a negative value and set errno
  1059.  * to indicate the error.  If not, return a nonnegative value.
  1060.  */
  1061. static int
  1062. expand_at_signs (buf, size, fp)
  1063.     char *buf;
  1064.     off_t size;
  1065.     FILE *fp;
  1066. {
  1067.     char *cp, *end;
  1068.  
  1069.     errno = 0;
  1070.     for (cp = buf, end = buf + size; cp < end; cp++)
  1071.     {
  1072.     if (*cp == '@')
  1073.     {
  1074.         if (putc ('@', fp) == EOF && errno != 0)
  1075.         return EOF;
  1076.     }
  1077.     if (putc (*cp, fp) == EOF && errno != 0)
  1078.         return (EOF);
  1079.     }
  1080.     return (1);
  1081. }
  1082.  
  1083. /*
  1084.  * Write an update message to (potentially) the screen and the log file.
  1085.  */
  1086. static void
  1087. add_log (ch, fname)
  1088.     int ch;
  1089.     char *fname;
  1090. {
  1091.     if (!really_quiet)            /* write to terminal */
  1092.     {
  1093.     char buf[2];
  1094.     buf[0] = ch;
  1095.     buf[1] = ' ';
  1096.     cvs_output (buf, 2);
  1097.     if (repos_len)
  1098.     {
  1099.         cvs_output (repository + repos_len + 1, 0);
  1100.         cvs_output ("/", 1);
  1101.     }
  1102.     else if (repository[0] != '\0')
  1103.     {
  1104.         cvs_output (repository, 0);
  1105.         cvs_output ("/", 1);
  1106.     }
  1107.     cvs_output (fname, 0);
  1108.     cvs_output ("\n", 1);
  1109.     }
  1110.  
  1111.     if (repos_len)            /* write to logfile */
  1112.     (void) fprintf (logfp, "%c %s/%s\n", ch,
  1113.             repository + repos_len + 1, fname);
  1114.     else if (repository[0])
  1115.     (void) fprintf (logfp, "%c %s/%s\n", ch, repository, fname);
  1116.     else
  1117.     (void) fprintf (logfp, "%c %s\n", ch, fname);
  1118. }
  1119.  
  1120. /*
  1121.  * This is the recursive function that walks the argument directory looking
  1122.  * for sub-directories that have CVS administration files in them and updates
  1123.  * them recursively.
  1124.  * 
  1125.  * Note that we do not follow symbolic links here, which is a feature!
  1126.  */
  1127. static int
  1128. import_descend_dir (message, dir, vtag, targc, targv)
  1129.     char *message;
  1130.     char *dir;
  1131.     char *vtag;
  1132.     int targc;
  1133.     char *targv[];
  1134. {
  1135.     struct saved_cwd cwd;
  1136.     char *cp;
  1137.     int ierrno, err;
  1138.  
  1139.     if (islink (dir))
  1140.     return (0);
  1141.     if (save_cwd (&cwd))
  1142.     {
  1143.     fperror (logfp, 0, 0, "ERROR: cannot get working directory");
  1144.     return (1);
  1145.     }
  1146.     if (repository[0] == '\0')
  1147.     (void) strcpy (repository, dir);
  1148.     else
  1149.     {
  1150.     (void) strcat (repository, "/");
  1151.     (void) strcat (repository, dir);
  1152.     }
  1153. #ifdef CLIENT_SUPPORT
  1154.     if (!quiet && !client_active)
  1155. #else
  1156.     if (!quiet)
  1157. #endif
  1158.     error (0, 0, "Importing %s", repository);
  1159.  
  1160.     if (chdir (dir) < 0)
  1161.     {
  1162.     ierrno = errno;
  1163.     fperror (logfp, 0, ierrno, "ERROR: cannot chdir to %s", repository);
  1164.     error (0, ierrno, "ERROR: cannot chdir to %s", repository);
  1165.     err = 1;
  1166.     goto out;
  1167.     }
  1168. #ifdef CLIENT_SUPPORT
  1169.     if (!client_active && !isdir (repository))
  1170. #else
  1171.     if (!isdir (repository))
  1172. #endif
  1173.     {
  1174.     char rcs[PATH_MAX];
  1175.  
  1176.     (void) sprintf (rcs, "%s%s", repository, RCSEXT);
  1177.     if (isfile (repository) || isfile(rcs))
  1178.     {
  1179.         fperror (logfp, 0, 0, "ERROR: %s is a file, should be a directory!",
  1180.              repository);
  1181.         error (0, 0, "ERROR: %s is a file, should be a directory!",
  1182.            repository);
  1183.         err = 1;
  1184.         goto out;
  1185.     }
  1186.     if (noexec == 0 && CVS_MKDIR (repository, 0777) < 0)
  1187.     {
  1188.         ierrno = errno;
  1189.         fperror (logfp, 0, ierrno,
  1190.              "ERROR: cannot mkdir %s -- not added", repository);
  1191.         error (0, ierrno,
  1192.            "ERROR: cannot mkdir %s -- not added", repository);
  1193.         err = 1;
  1194.         goto out;
  1195.     }
  1196.     }
  1197.     err = import_descend (message, vtag, targc, targv);
  1198.   out:
  1199.     if ((cp = strrchr (repository, '/')) != NULL)
  1200.     *cp = '\0';
  1201.     else
  1202.     repository[0] = '\0';
  1203.     if (restore_cwd (&cwd, NULL))
  1204.       exit (EXIT_FAILURE);
  1205.     free_cwd (&cwd);
  1206.     return (err);
  1207. }
  1208.